package com.yc.spring.mvc.core;


import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import com.yc.spring.mvc.core.annotation.Autowired;
import com.yc.spring.mvc.core.annotation.Component;
import com.yc.spring.mvc.core.annotation.Controller;
import com.yc.spring.mvc.core.annotation.RequestMapping;

/**
 * 1.读取配置文件 获取要扫描的基础路径
 * 2.扫描包，获取类路径
 * 3.初始化需要IoC容器管理的类，并交给IoC容器管理
 * 4.执行依赖注入，即完成@Autowired
 * 5.构建HandlerMapping.完成请求地址URL与对应方法之间的映射@Controller
 * 6.解析请求，完成形参注值
 * company 源辰信息
 * @author navy
 * @date 2021年3月30日
 */
public class FrameworkCore {
	private String contextConfigLocation; // 配置文件路径
	private  String basePackge;
	private Set<String> classNames = new HashSet<String>();
	private Map<String, Object> instanceObject = new HashMap<String, Object>();
	private Map<String, HandlerMapperInfo> handlerMapper = new HashMap<String, HandlerMapperInfo>();

	public FrameworkCore(String contextConfigLocation) {
		this.contextConfigLocation = contextConfigLocation;
		init();
	}
	
	/**
	 * 初始化方法
	 */
	private void init() {
		// 读取配置文件 获取扫描的基础路径
		doLoadConfig();
		
		// 扫描包 获取类路径
		doScannerPackage();
		
		//classNames.forEach(System.out::println);
		
		// 初始化需要Ioc容器管理的类，并交给Ioc容器管理
		doInstanceObject();
		
		// 执行依赖注入 即完成@Autowired
		doAutowired();
		
		// 构造Handldermapping完成请求地址URL与对应方法之间的映射@Controller @Requestmapping
		initHandlerMapping();
		/*handlerMapper.forEach((key,  val) -> {
			System.out.println(key + ":" + val);
		});*/
		
	}
	
	// 读取配置文件 获取扫描的基础路径
	private void doLoadConfig() {
		try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation)) {
				Properties properties = new Properties(); 
				properties.load(is);
				basePackge = properties.getProperty("basePackage").trim();
			} catch (IOException e) {
				e.printStackTrace();
			}	
	}
	 
	// 扫描包 获取类路径
	private void doScannerPackage() {
		if (StringUtil.chekNUll(basePackge)) {
			throw new RuntimeException("读取配置文件失败， 请配置contextConfigLocation参数以及basePackage属性...");
		}
		
		URL url = this.getClass().getClassLoader().getResource(basePackge.replaceAll("\\.", "/"));
		
		File dist = new File(url.getFile()); // 将这个路径转成文件目录
		
		getClassInfo(basePackge, dist);
		}
		
	
	/**
	 * 获取指定路径下的子文件和目录，class文件
	 * @param basePackage2
	 * @param dist
	 */
	private void getClassInfo(String basePackge, File dist) {
		if (dist.exists() && dist.isDirectory()) {  // 说明你是一个目录
			for (File fl : dist.listFiles()) {
				if (fl.isDirectory()) {
					getClassInfo(basePackge + "." + fl.getName(), fl);
				} else {
					classNames.add(basePackge + "." + fl.getName().replace(".class", "")) ;
				}
			}
		}
	}

	// 初始化需要Ioc容器管理的类，并交给Ioc容器管理
	private void doInstanceObject() {
		if (classNames.isEmpty()) {
			return;
		}
		
		Class<?> cls = null;
		Object instance = null;
		String beanName = null;
		String temp = null;
		Class<?>[] interfaces = null;
		
		for (String className : classNames) {
			try {
				cls = Class.forName(className);
				beanName = this.toFirstLowserCase(cls.getSimpleName()); // 默认情况下就是将类的第一个字母小写
				
				if (cls.isAnnotationPresent(Controller.class)) {   // 这个类上有@Controller注解吗
					temp = cls.getAnnotation(Controller.class).value(); // 有配置bean的名字吗
					if (!StringUtil.chekNUll(temp)) {
						beanName = temp;
					}
					instanceObject.put(beanName, cls.newInstance());
				} else if (cls.isAnnotationPresent(Component.class)) {
					temp = cls.getAnnotation(Component.class).value();
					if (!StringUtil.chekNUll(temp)) {
						beanName = temp;
					}
					instance = cls.newInstance();
					instanceObject.put(beanName, cls.newInstance());
					
					// 这个类实现了哪些接口
					interfaces = cls.getInterfaces();
					
					if (interfaces == null || interfaces.length <= 0) {
						continue;
					}
					
					// 我已经将这个接口名bean, 已经实现类为值
					for (Class<?> ites : interfaces) {
						instanceObject.put(ites.getSimpleName(), instance);
					}
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	private String toFirstLowserCase(String name) {
		char[] chs = name.toCharArray();
		chs[0] += 32;
		return String.valueOf(chs);
	}

	// 执行依赖注入 即完成@Autowired
	private void doAutowired() {
		if (instanceObject.isEmpty()) {
			return;
		}
		
		Field[] fields = null;
		Class<?> cls = null;
		Autowired awd = null;
		String beanName = null;
		Object obj = null;
		
		for (Entry<String, Object> entry : instanceObject.entrySet()) {
			cls = entry.getValue().getClass();
			fields = cls.getDeclaredFields();
			
			if (fields == null || fields.length <= 0) {
				continue;
			}
			
			for (Field fd : fields) {
				if (!fd.isAnnotationPresent(Autowired.class)) {
					continue;
				}
				
				awd = fd.getAnnotation(Autowired.class);
				beanName = awd.value().trim(); // 先获取整个注解上有没有配置value属性， 即要注入的bean的名字
				
				fd.setAccessible(true); // 强吻属性
				
				if (StringUtil.chekNUll(beanName)) { // 说明用户没有指定， 则更加类名注入
					beanName = fd.getType().getSimpleName();
					obj = instanceObject.get(beanName) == null ? instanceObject.get(toFirstLowserCase(beanName)) : instanceObject.get(beanName);
					
					try {
						fd.set(entry.getValue(), obj);  //这个属性是私有的 这样注入失败
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}	
				} else {
					if (!instanceObject.containsKey(beanName)) {
						throw new RuntimeException(cls.getName() + "." + fd.getName() +" 注值失败， 没有对应的实体类" + beanName);
					}
					
					try {
						fd.set(entry.getValue(), obj);  //这个属性是私有的 这样注入失败
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	
	
	// 构造Handldermapping完成请求地址URL与对应方法之间的映射@Controller @Requestmapping
	private void initHandlerMapping() {
		if (instanceObject.isEmpty()) {
			return;
		}
		
		Class<?> cls = null;
		Method[] methods = null;
		RequestMapping requestMapping = null;
		String baseUrl = "";
		String url = null;
		
		for (Entry<String, Object> entry : instanceObject.entrySet()) {
			cls = entry.getValue().getClass();
			
			if (!cls.isAnnotationPresent(Controller.class)) {
				continue;
			}
			
			requestMapping = cls.getAnnotation(RequestMapping.class);
			
			if (requestMapping != null ) { // 说明控制器类上有配置
				baseUrl = requestMapping.value();
				
				if (!baseUrl.startsWith("/")) {
					baseUrl = "/" + baseUrl;
				}
				
				// 获取控制类下面的所有方法
				methods = cls.getDeclaredMethods();
				
				if (methods == null || methods.length <= 0) {
					continue;
				}
				
				for (Method md : methods) {
					if (!md.isAnnotationPresent(RequestMapping.class)) {
						continue;
					}
					
					requestMapping = md.getAnnotation(RequestMapping.class);
					url = requestMapping.value();
					
					if (!url.startsWith("/")) {
						url = "/" + url;
					}
					
					url = baseUrl + url;
					handlerMapper.put(url.replaceAll("/+", "/"), new HandlerMapperInfo(md, entry.getValue()));
				}
			}
		}
		
		
		
	}

	public String getContextConfigLocation() {
		return contextConfigLocation;
	}

	public String getBasePackge() {
		return basePackge;
	}

	public Set<String> getClassNames() {
		return classNames;
	}

	public Map<String, Object> getInstanceObject() {
		return instanceObject;
	}

	public Map<String, HandlerMapperInfo> getHandlerMapper() {
		return handlerMapper;
	}

	public HandlerMapperInfo getMapper(String url) {
		return handlerMapper.getOrDefault(url, null);
	}

	
}
